﻿namespace DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    using System.Runtime.Serialization;
    using System.ServiceModel.DomainServices;
    using System.ServiceModel.DomainServices.Server;
    using System.Threading;

    internal sealed class EntityProxyGenerator : DataContractProxyGenerator
    {
        private ICollection<DomainServiceDescription> _allDomainServiceDescriptions;
        private DomainServiceDescriptionAggregate _domainServiceDescriptionAggregate;
        private bool _generateGetIdentity;
        private List<PropertyDescriptor> _keyProperties;
        private Type _visibleBaseType;

        public EntityProxyGenerator(CodeDomClientCodeGenerator proxyGenerator, Type entityType, ICollection<DomainServiceDescription> allDomainServiceDescriptions, IDictionary<Type, CodeTypeDeclaration> typeMapping) : base(proxyGenerator, entityType, typeMapping)
        {
            System.Func<DomainServiceDescription, bool> func = null;
            if (func == null)
            {
                func = dsd => dsd.EntityTypes.Contains<Type>(entityType);
            }
            this._domainServiceDescriptionAggregate = new DomainServiceDescriptionAggregate(Enumerable.Where<DomainServiceDescription>(allDomainServiceDescriptions, func));
            this._allDomainServiceDescriptions = allDomainServiceDescriptions;
            this._visibleBaseType = this._domainServiceDescriptionAggregate.GetEntityBaseType(base.Type);
            this._generateGetIdentity = !this.IsDerivedType;
            this._keyProperties = new List<PropertyDescriptor>();
        }

        protected override void AddBaseTypes(CodeNamespace ns)
        {
            if (this._visibleBaseType != null)
            {
                base.ProxyClass.BaseTypes.Add(CodeGenUtilities.GetTypeReference(this._visibleBaseType, base.ClientProxyGenerator, base.ProxyClass));
            }
            else
            {
                base.ProxyClass.BaseTypes.Add(CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.Entity", ns.Name, false));
            }
        }

        protected override bool CanGenerateProperty(PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor.Attributes[typeof(ExcludeAttribute)] != null)
            {
                return false;
            }
            if (propertyDescriptor.Attributes[typeof(ExternalReferenceAttribute)] == null)
            {
                if (!base.CanGenerateProperty(propertyDescriptor))
                {
                    if (propertyDescriptor.ExplicitAttributes()[typeof(KeyAttribute)] != null)
                    {
                        base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_EntityKey_PropertyNotSerializable, new object[] { base.Type, propertyDescriptor.Name }));
                        return false;
                    }
                    Type elementType = System.ServiceModel.DomainServices.TypeUtility.GetElementType(propertyDescriptor.PropertyType);
                    if (!this._domainServiceDescriptionAggregate.EntityTypes.Contains<Type>(elementType) || (propertyDescriptor.Attributes[typeof(AssociationAttribute)] == null))
                    {
                        return false;
                    }
                }
                if (!this.CanGeneratePropertyIfPolymorphic(propertyDescriptor))
                {
                    return false;
                }
            }
            return true;
        }

        protected override bool CanGeneratePropertyIfPolymorphic(PropertyDescriptor pd)
        {
            PropertyInfo property = base.Type.GetProperty(pd.Name);
            if ((property != null) && (this.IsMethodPolymorphic(property.GetGetMethod()) || this.IsMethodPolymorphic(property.GetSetMethod())))
            {
                foreach (Type type in this.GetVisibleBaseTypes(base.Type))
                {
                    if (type.GetProperty(pd.Name) != null)
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        private void GenEntityAssocationProperty(CodeTypeDeclaration proxyClass, PropertyDescriptor pd, AssociationAttribute associationAttribute, bool isExternal)
        {
            System.Func<Type, bool> func = null;
            Type associationType = IsCollectionType(pd.PropertyType) ? System.ServiceModel.DomainServices.TypeUtility.GetElementType(pd.PropertyType) : pd.PropertyType;
            if (!CodeGenUtilities.RegisterTypeName(associationType, base.Type.Namespace))
            {
                if (func == null)
                {
                    func = entity => entity.Namespace == associationType.Namespace;
                }
                //foreach (Type type in Enumerable.Where<Type>(from dsd in base.ClientProxyGenerator.DomainServiceDescriptions select dsd.EntityTypes, func).Distinct<Type>())
                //{
                //    CodeGenUtilities.RegisterTypeName(type, base.Type.Namespace);
                //}
            }
            if (!IsCollectionType(pd.PropertyType))
            {
                this.GenerateSingletonAssociation(proxyClass, pd, associationAttribute, isExternal);
            }
            else
            {
                this.GenerateCollectionSideAssociation(proxyClass, pd, associationAttribute, isExternal);
            }
        }

        public override void Generate()
        {
            if (this.VerifySharedEntityRoot(base.Type))
            {
                base.Generate();
            }
        }

        protected override void GenerateAdditionalMembers()
        {
            if (this._generateGetIdentity)
            {
                this.GenGetIdentityMethod(base.ProxyClass, this._keyProperties);
            }
            new CustomMethodProxyGenerator(base.ClientProxyGenerator, base.ProxyClass, base.Type, this._allDomainServiceDescriptions, base.NotificationMethodGen).Generate();
            base.GenerateAdditionalMembers();
        }

        private CodeMemberMethod GenerateAttach(CodeTypeDeclaration proxyClass, Type entityType, AssociationAttribute assoc, PropertyDescriptor pd)
        {
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "Attach" + pd.Name,
                Attributes = MemberAttributes.Private,
                ReturnType = CodeGenUtilities.GetTypeReference(typeof(void), base.ClientProxyGenerator, proxyClass)
            };
            CodeParameterDeclarationExpression expression = new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(entityType, base.ClientProxyGenerator, proxyClass), "entity");
            method.Parameters.Add(expression);
            CodeVariableReferenceExpression targetObject = new CodeVariableReferenceExpression(expression.Name);
            string name = GetReverseAssociation(pd, assoc).Name;
            if (!IsCollectionType(pd.PropertyType))
            {
                method.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(targetObject, name), "Add", new CodeExpression[] { new CodeThisReferenceExpression() })));
            }
            else
            {
                method.Statements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(targetObject, name), new CodeThisReferenceExpression()));
            }
            proxyClass.Members.Add(method);
            return method;
        }

        private void GenerateCollectionSideAssociation(CodeTypeDeclaration proxyClass, PropertyDescriptor pd, AssociationAttribute associationAttribute, bool isExternal)
        {
            CodeTypeReference createType = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.EntityCollection", base.Type.Namespace, false);
            Type elementType = System.ServiceModel.DomainServices.TypeUtility.GetElementType(pd.PropertyType);
            if (isExternal)
            {
                CodeTypeReference reference3 = new CodeTypeReference(elementType.FullName);
                createType.TypeArguments.Add(reference3);
                reference3.Options = CodeTypeReferenceOptions.GlobalReference;
            }
            else
            {
                createType.TypeArguments.Add(CodeGenUtilities.GetTypeReference(elementType, base.ClientProxyGenerator, proxyClass));
            }
            CodeTypeReference reference2 = createType;
            CodeMemberField field = new CodeMemberField {
                Attributes = MemberAttributes.Private,
                Name = CodeGenUtilities.MakeCompliantFieldName(pd.Name),
                Type = reference2
            };
            proxyClass.Members.Add(field);
            string[] thisKeyProps = associationAttribute.ThisKeyMembers.ToArray<string>();
            string[] otherKeyProps = associationAttribute.OtherKeyMembers.ToArray<string>();
            CodeMemberMethod method = this.GenerateFilterMethod(proxyClass, pd.Name, elementType, thisKeyProps, otherKeyProps, isExternal);
            CodeMemberProperty prop = new CodeMemberProperty {
                Name = pd.Name,
                Attributes = MemberAttributes.Public | MemberAttributes.Final,
                Type = createType,
                HasGet = true
            };
            string comment = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_Collection_Association_Property_Summary_Comment, new object[] { elementType.Name });
            prop.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            List<Attribute> source = pd.ExplicitAttributes().Cast<Attribute>().ToList<Attribute>();
            ReadOnlyAttribute attribute = source.OfType<ReadOnlyAttribute>().SingleOrDefault<ReadOnlyAttribute>();
            if ((attribute != null) && !source.OfType<EditableAttribute>().Any<EditableAttribute>())
            {
                source.Add(new EditableAttribute(!attribute.IsReadOnly));
            }
            CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, proxyClass, ex => string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, new object[] { ex.Message, prop.Name, proxyClass.Name, ex.InnerException.Message }), from a in source
                where a.GetType() != typeof(DataMemberAttribute)
                select a, prop.CustomAttributes, prop.Comments);
            CodeExpression condition = CodeGenUtilities.MakeEqualToNull(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name));
            CodeExpression expression2 = CodeGenUtilities.MakeDelegateCreateExpression(base.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Func"), method.Name);
            PropertyDescriptor reverseAssociation = GetReverseAssociation(pd, associationAttribute);
            if ((reverseAssociation != null) && this.CanGenerateProperty(reverseAssociation))
            {
                if (IsCollectionType(pd.PropertyType))
                {
                    CodeMemberMethod method2 = this.GenerateAttach(proxyClass, elementType, associationAttribute, pd);
                    CodeMemberMethod method3 = this.GenerateDetach(proxyClass, elementType, associationAttribute, pd);
                    CodeExpression expression3 = CodeGenUtilities.MakeDelegateCreateExpression(base.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Action"), method2.Name);
                    CodeExpression expression4 = CodeGenUtilities.MakeDelegateCreateExpression(base.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Action"), method3.Name);
                    CodeAssignStatement statement = new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name), new CodeObjectCreateExpression(createType, new CodeExpression[] { new CodeThisReferenceExpression(), new CodePrimitiveExpression(prop.Name), expression2, expression3, expression4 }));
                    prop.GetStatements.Add(new CodeConditionStatement(condition, new CodeStatement[] { statement }));
                }
            }
            else
            {
                CodeAssignStatement statement2 = new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name), new CodeObjectCreateExpression(createType, new CodeExpression[] { new CodeThisReferenceExpression(), new CodePrimitiveExpression(prop.Name), expression2 }));
                prop.GetStatements.Add(new CodeConditionStatement(condition, new CodeStatement[] { statement2 }));
            }
            prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name)));
            proxyClass.Members.Add(prop);
            proxyClass.Members.Add(method);
        }

        private CodeMemberMethod GenerateDetach(CodeTypeDeclaration proxyClass, Type entityType, AssociationAttribute assoc, PropertyDescriptor pd)
        {
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "Detach" + pd.Name,
                Attributes = MemberAttributes.Private,
                ReturnType = CodeGenUtilities.GetTypeReference(typeof(void), base.ClientProxyGenerator, proxyClass)
            };
            CodeParameterDeclarationExpression expression = new CodeParameterDeclarationExpression(CodeGenUtilities.GetTypeReference(entityType, base.ClientProxyGenerator, proxyClass), "entity");
            method.Parameters.Add(expression);
            CodeVariableReferenceExpression targetObject = new CodeVariableReferenceExpression(expression.Name);
            string name = GetReverseAssociation(pd, assoc).Name;
            if (!IsCollectionType(pd.PropertyType))
            {
                method.Statements.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(targetObject, name), "Remove", new CodeExpression[] { new CodeThisReferenceExpression() })));
            }
            else
            {
                method.Statements.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(targetObject, name), new CodePrimitiveExpression(null)));
            }
            proxyClass.Members.Add(method);
            return method;
        }

        private CodeMemberMethod GenerateFilterMethod(CodeTypeDeclaration proxyClass, string targetName, Type entityType, string[] thisKeyProps, string[] otherKeyProps, bool isExternal)
        {
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "Filter" + targetName,
                Attributes = MemberAttributes.Private,
                ReturnType = CodeGenUtilities.GetTypeReference(typeof(bool), base.ClientProxyGenerator, proxyClass)
            };
            method.Parameters.Add(new CodeParameterDeclarationExpression(isExternal ? new CodeTypeReference(entityType.FullName) : CodeGenUtilities.GetTypeReference(entityType, base.ClientProxyGenerator, proxyClass), "entity"));
            CodeExpression left = null;
            for (int i = 0; i < thisKeyProps.Length; i++)
            {
                CodeExpression expression2;
                if (base.ClientProxyGenerator.IsCSharp)
                {
                    expression2 = new CodeBinaryOperatorExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("entity"), otherKeyProps[i]), CodeBinaryOperatorType.ValueEquality, new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), thisKeyProps[i]));
                }
                else
                {
                    expression2 = CodeGenUtilities.MakeEqual(typeof(object), new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("entity"), otherKeyProps[i]), new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), thisKeyProps[i]), base.ClientProxyGenerator.IsCSharp);
                }
                if (left == null)
                {
                    left = expression2;
                }
                else
                {
                    left = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BooleanAnd, expression2);
                }
            }
            method.Statements.Add(new CodeMethodReturnStatement(left));
            return method;
        }

        protected override bool GenerateNonSerializableProperty(PropertyDescriptor propertyDescriptor)
        {
            AttributeCollection attributes = propertyDescriptor.ExplicitAttributes();
            AssociationAttribute associationAttribute = (AssociationAttribute) attributes[typeof(AssociationAttribute)];
            bool isExternal = attributes[typeof(ExternalReferenceAttribute)] != null;
            if (associationAttribute != null)
            {
                this.GenEntityAssocationProperty(base.ProxyClass, propertyDescriptor, associationAttribute, isExternal);
                return true;
            }
            return false;
        }

        protected override void GenerateProperty(PropertyDescriptor propertyDescriptor)
        {
            base.GenerateProperty(propertyDescriptor);
            if (propertyDescriptor.ExplicitAttributes()[typeof(KeyAttribute)] != null)
            {
                this._keyProperties.Add(propertyDescriptor);
            }
        }

        private void GenerateSingletonAssociation(CodeTypeDeclaration proxyClass, PropertyDescriptor pd, AssociationAttribute associationAttribute, bool isExternal)
        {
            CodeTypeReference reference = isExternal ? new CodeTypeReference(pd.PropertyType.FullName) : CodeGenUtilities.GetTypeReference(pd.PropertyType, base.ClientProxyGenerator, proxyClass);
            CodeTypeReference createType = CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.EntityRef", base.Type.Namespace, false);
            createType.TypeArguments.Add(reference);
            CodeMemberField field = new CodeMemberField {
                Attributes = MemberAttributes.Private,
                Name = CodeGenUtilities.MakeCompliantFieldName(pd.Name),
                Type = createType
            };
            proxyClass.Members.Add(field);
            CodeMemberProperty prop = new CodeMemberProperty {
                Attributes = MemberAttributes.Public | MemberAttributes.Final,
                Name = pd.Name,
                Type = reference,
                HasGet = true,
                HasSet = true
            };
            if (isExternal && !associationAttribute.IsForeignKey)
            {
                prop.HasSet = false;
            }
            string format = prop.HasSet ? DomainServices.Tools.Resource.CodeGen_Entity_Singleton_Association_Property_Summary_Comment : DomainServices.Tools.Resource.CodeGen_Entity_Singleton_Association_ReadOnly_Property_Summary_Comment;
            string comment = string.Format(CultureInfo.CurrentCulture, format, new object[] { pd.PropertyType.Name });
            prop.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(comment, base.ClientProxyGenerator.IsCSharp));
            AttributeCollection source = pd.ExplicitAttributes();
            CustomAttributeGenerator.GenerateCustomAttributes(base.ClientProxyGenerator, proxyClass, ex => string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.ClientCodeGen_Attribute_ThrewException_CodeTypeMember, new object[] { ex.Message, prop.Name, proxyClass.Name, ex.InnerException.Message }), from a in source.Cast<Attribute>()
                where a.GetType() != typeof(DataMemberAttribute)
                select a, prop.CustomAttributes, prop.Comments);
            string[] thisKeyProps = associationAttribute.ThisKeyMembers.ToArray<string>();
            string[] otherKeyProps = associationAttribute.OtherKeyMembers.ToArray<string>();
            CodeMemberMethod method = this.GenerateFilterMethod(proxyClass, pd.Name, pd.PropertyType, thisKeyProps, otherKeyProps, isExternal);
            CodeExpression targetObject = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
            targetObject = new CodePropertyReferenceExpression(targetObject, "Entity");
            CodeExpression condition = CodeGenUtilities.MakeEqualToNull(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name));
            CodeExpression expression3 = CodeGenUtilities.MakeDelegateCreateExpression(base.ClientProxyGenerator.IsCSharp, new CodeTypeReference("System.Func"), method.Name);
            CodeAssignStatement statement = new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name), new CodeObjectCreateExpression(createType, new CodeExpression[] { new CodeThisReferenceExpression(), new CodePrimitiveExpression(prop.Name), expression3 }));
            prop.GetStatements.Add(new CodeConditionStatement(condition, new CodeStatement[] { statement }));
            prop.GetStatements.Add(new CodeMethodReturnStatement(targetObject));
            if (prop.HasSet)
            {
                CodeConditionStatement statement6;
                PropertyDescriptor reverseAssociation = GetReverseAssociation(pd, associationAttribute);
                CodeStatement statement2 = null;
                CodeStatement statement3 = null;
                bool flag2 = (reverseAssociation != null) && this.CanGenerateProperty(reverseAssociation);
                if (!isExternal && flag2)
                {
                    string name = reverseAssociation.Name;
                    if (IsCollectionType(reverseAssociation.PropertyType))
                    {
                        statement2 = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("previous"), name), "Remove", new CodeExpression[] { new CodeThisReferenceExpression() }));
                        statement3 = new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodePropertyReferenceExpression(new CodePropertySetValueReferenceExpression(), name), "Add", new CodeExpression[] { new CodeThisReferenceExpression() }));
                    }
                    else
                    {
                        statement2 = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("previous"), name), new CodePrimitiveExpression(null));
                        statement3 = new CodeAssignStatement(new CodePropertyReferenceExpression(new CodePropertySetValueReferenceExpression(), name), new CodeThisReferenceExpression());
                    }
                }
                List<CodeStatement> list = null;
                List<CodeStatement> list2 = null;
                list = new List<CodeStatement>();
                if (associationAttribute.IsForeignKey)
                {
                    for (int i = 0; i < thisKeyProps.Length; i++)
                    {
                        list.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), thisKeyProps[i]), new CodePropertyReferenceExpression(new CodePropertySetValueReferenceExpression(), otherKeyProps[i])));
                    }
                    list2 = new List<CodeStatement>();
                    for (int j = 0; j < thisKeyProps.Length; j++)
                    {
                        Type propertyType = TypeDescriptor.GetProperties(base.Type).Find(thisKeyProps[j], false).PropertyType;
                        list2.Add(new CodeAssignStatement(new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), thisKeyProps[j]), new CodeDefaultValueExpression(CodeGenUtilities.GetTypeReference(propertyType, base.ClientProxyGenerator, proxyClass))));
                    }
                }
                CodeExpression expression4 = CodeGenUtilities.MakeNotEqual(null, new CodeVariableReferenceExpression("previous"), new CodePropertySetValueReferenceExpression(), base.ClientProxyGenerator.IsCSharp);
                prop.SetStatements.Add(new CodeVariableDeclarationStatement(prop.Type, "previous", new CodePropertyReferenceExpression(new CodeThisReferenceExpression(), pd.Name)));
                List<CodeStatement> list3 = new List<CodeStatement>();
                CodeStatement item = DataContractProxyGenerator.GeneratePropertySetterValidation(prop.Name);
                list3.Add(item);
                if (!isExternal && flag2)
                {
                    //List<CodeStatement> list4;
                    //list4 = new List<CodeStatement> {
                    //    new CodeAssignStatement(targetObject, new CodePrimitiveExpression(null)),
                    //    statement2,
                    //    new CodeConditionStatement(CodeGenUtilities.MakeNotEqualToNull(new CodeVariableReferenceExpression("previous")), list4.ToArray())
                    //};
                }
                CodeAssignStatement statement5 = new CodeAssignStatement(targetObject, new CodePropertySetValueReferenceExpression());
                if (associationAttribute.IsForeignKey)
                {
                    statement6 = new CodeConditionStatement(CodeGenUtilities.MakeNotEqualToNull(new CodePropertySetValueReferenceExpression()), list.ToArray(), list2.ToArray());
                    list3.Add(statement6);
                    if (!isExternal)
                    {
                        list3.Add(statement5);
                        if (flag2)
                        {
                            list3.Add(new CodeConditionStatement(CodeGenUtilities.MakeNotEqualToNull(new CodePropertySetValueReferenceExpression()), new CodeStatement[] { statement3 }));
                        }
                    }
                }
                else
                {
                    list3.Add(statement5);
                    if (flag2)
                    {
                        list3.Add(new CodeConditionStatement(CodeGenUtilities.MakeNotEqualToNull(new CodePropertySetValueReferenceExpression()), new CodeStatement[] { statement3 }));
                    }
                    statement6 = new CodeConditionStatement(CodeGenUtilities.MakeNotEqualToNull(new CodePropertySetValueReferenceExpression()), list.ToArray());
                }
                if (!isExternal)
                {
                    list3.Add(new CodeExpressionStatement(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), "RaisePropertyChanged", new CodeExpression[] { new CodePrimitiveExpression(prop.Name) })));
                }
                prop.SetStatements.Add(new CodeConditionStatement(expression4, list3.ToArray()));
            }
            proxyClass.Members.Add(prop);
            proxyClass.Members.Add(method);
        }

        private void GenGetIdentityMethod(CodeTypeDeclaration proxyClass, List<PropertyDescriptor> keyProperties)
        {
            CodeMemberMethod method = new CodeMemberMethod {
                Name = "GetIdentity",
                ReturnType = CodeGenUtilities.GetTypeReference(typeof(object), base.ClientProxyGenerator, proxyClass),
                Attributes = MemberAttributes.Public | MemberAttributes.Override
            };
            proxyClass.Members.Add(method);
            method.Comments.AddRange(CodeGenUtilities.GenerateSummaryCodeComment(DomainServices.Tools.Resource.CodeGen_GetIdentify_Method_Summary_Comment, base.ClientProxyGenerator.IsCSharp));
            method.Comments.AddRange(CodeGenUtilities.GenerateReturnsCodeComment(DomainServices.Tools.Resource.CodeGen_GetIdentify_Method_Returns_Comment, base.ClientProxyGenerator.IsCSharp));
            CodeFieldReferenceExpression[] parameters = new CodeFieldReferenceExpression[this._keyProperties.Count];
            CodeExpression left = null;
            int num = 0;
            for (int i = 0; i < parameters.Length; i++)
            {
                PropertyDescriptor descriptor = keyProperties[i];
                CodeFieldReferenceExpression expression2 = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), CodeGenUtilities.MakeCompliantFieldName(descriptor.Name));
                Type propertyType = keyProperties[i].PropertyType;
                CodeExpression right = null;
                if (!propertyType.IsValueType || System.ServiceModel.DomainServices.TypeUtility.IsNullableType(propertyType))
                {
                    num++;
                    right = CodeGenUtilities.MakeEqualToNull(expression2);
                    if (left != null)
                    {
                        left = new CodeBinaryOperatorExpression(left, CodeBinaryOperatorType.BooleanOr, right);
                    }
                    else
                    {
                        left = right;
                    }
                }
                parameters[i] = expression2;
            }
            if (this._keyProperties.Count == 1)
            {
                method.Statements.Add(new CodeMethodReturnStatement(parameters[0]));
            }
            else
            {
                if (num > 0)
                {
                    CodeConditionStatement statement = new CodeConditionStatement(left, new CodeStatement[] { new CodeMethodReturnStatement(new CodePrimitiveExpression(null)) });
                    method.Statements.Add(statement);
                }
                CodeMethodInvokeExpression expression = new CodeMethodInvokeExpression(new CodeTypeReferenceExpression(CodeGenUtilities.GetTypeReference("System.ServiceModel.DomainServices.Client.EntityKey", base.Type.Namespace, false)), "Create", parameters);
                method.Statements.Add(new CodeMethodReturnStatement(expression));
            }
        }

        protected override IEnumerable<Type> GetDerivedTypes()
        {
            return (from t in this._domainServiceDescriptionAggregate.EntityTypes
                where (t != base.Type) && base.Type.IsAssignableFrom(t)
                select t);
        }

        private static PropertyDescriptor GetReverseAssociation(PropertyDescriptor propertyDescriptor, AssociationAttribute assocAttrib)
        {
            foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(System.ServiceModel.DomainServices.TypeUtility.GetElementType(propertyDescriptor.PropertyType)))
            {
                if (!(descriptor.Name == propertyDescriptor.Name))
                {
                    AssociationAttribute attribute = descriptor.Attributes[typeof(AssociationAttribute)] as AssociationAttribute;
                    if ((attribute != null) && (attribute.Name == assocAttrib.Name))
                    {
                        return descriptor;
                    }
                }
            }
            return null;
        }

        protected override string GetSummaryComment()
        {
            string str = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_Class_Summary_Comment, new object[] { base.Type.Name });
            if (this._domainServiceDescriptionAggregate.IsShared)
            {
                string str2 = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_Class_Shared_Summary_Comment, new object[0]);
                str = str + Environment.NewLine + str2;
                foreach (DomainServiceDescription description in this._domainServiceDescriptionAggregate.DomainServiceDescriptions)
                {
                    string str3 = string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.CodeGen_Entity_Class_Context_Summary_Comment, new object[] { DomainServiceProxyGenerator.DomainContextTypeName(description) });
                    str = str + Environment.NewLine + str3;
                }
            }
            return str;
        }

        private IEnumerable<Type> GetVisibleBaseTypes(Type entityType)
        {
            List<Type> list = new List<Type>();
            for (Type type = this._domainServiceDescriptionAggregate.GetEntityBaseType(entityType); type != null; type = this._domainServiceDescriptionAggregate.GetEntityBaseType(type))
            {
                list.Add(type);
            }
            return list;
        }

        private static bool IsCollectionType(Type t)
        {
            return typeof(IEnumerable).IsAssignableFrom(t);
        }

        private bool IsMethodPolymorphic(MethodInfo methodInfo)
        {
            if ((methodInfo != null) && (methodInfo.DeclaringType == base.Type))
            {
                if (methodInfo.IsVirtual && !methodInfo.IsFinal)
                {
                    return true;
                }
                if (base.Type.BaseType != null)
                {
                    Type[] types = (from p in methodInfo.GetParameters() select p.ParameterType).ToArray<Type>();
                    if (base.Type.BaseType.GetMethod(methodInfo.Name, types) != null)
                    {
                        return true;
                    }
                }
            }
            return false;
        }

        protected override bool IsPropertyShared(PropertyDescriptor pd)
        {
            if (!base.IsPropertyShared(pd))
            {
                return false;
            }
            if (pd.ExplicitAttributes()[typeof(KeyAttribute)] != null)
            {
                this._generateGetIdentity = false;
            }
            return true;
        }

        protected override void OnPropertySkipped(PropertyDescriptor pd)
        {
            if (pd.ExplicitAttributes()[typeof(KeyAttribute)] != null)
            {
                base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_EntityKey_PropertyNotSerializable, new object[] { base.Type, pd.Name }));
            }
        }

        protected override bool ShouldDeclareProperty(PropertyDescriptor pd)
        {
            if (!base.ShouldDeclareProperty(pd))
            {
                return false;
            }
            if (!this.ShouldFlattenProperty(pd))
            {
                return false;
            }
            AttributeCollection attributes = pd.ExplicitAttributes();
            Type propertyType = pd.PropertyType;
            if ((attributes[typeof(KeyAttribute)] != null) && !System.ServiceModel.DomainServices.TypeUtility.IsPredefinedSimpleType(propertyType))
            {
                base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_EntityKey_KeyTypeNotSupported, new object[] { base.Type, pd.Name, propertyType }));
                return false;
            }
            return true;
        }

        private bool ShouldFlattenProperty(PropertyDescriptor propertyDescriptor)
        {
            Type componentType = propertyDescriptor.ComponentType;
            if (componentType == base.Type)
            {
                return true;
            }
            if (!componentType.IsAssignableFrom(base.Type))
            {
                return true;
            }
            if (!this._domainServiceDescriptionAggregate.EntityTypes.Contains<Type>(componentType))
            {
                for (Type type2 = base.Type.BaseType; type2 != null; type2 = type2.BaseType)
                {
                    if (type2 == componentType)
                    {
                        return true;
                    }
                    if (this._domainServiceDescriptionAggregate.EntityTypes.Contains<Type>(type2))
                    {
                        break;
                    }
                }
            }
            return false;
        }

        protected override void ValidateTypeAttributes(AttributeCollection typeAttributes)
        {
            if (typeAttributes[typeof(RoundtripOriginalAttribute)] != null)
            {
                Type rootEntityType = this._domainServiceDescriptionAggregate.GetRootEntityType(base.Type);
                if (rootEntityType.Attributes()[typeof(RoundtripOriginalAttribute)] == null)
                {
                    base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_RoundtripOriginalOnBaseType, new object[] { base.Type, rootEntityType }));
                }
            }
        }

        private bool VerifySharedEntityRoot(Type entityType)
        {
            if (this._domainServiceDescriptionAggregate.IsShared)
            {
                Type type = null;
                DomainServiceDescription description = null;
                foreach (DomainServiceDescription description2 in this._domainServiceDescriptionAggregate.DomainServiceDescriptions)
                {
                    Type rootEntityType = description2.GetRootEntityType(entityType);
                    if (type == null)
                    {
                        type = rootEntityType;
                        description = description2;
                    }
                    else if (type != rootEntityType)
                    {
                        base.ClientProxyGenerator.LogError(string.Format(CultureInfo.CurrentCulture, DomainServices.Tools.Resource.EntityCodeGen_SharedEntityMustBeLeastDerived, new object[] { type, description.DomainServiceType, rootEntityType, description2.DomainServiceType, entityType }));
                        return false;
                    }
                }
            }
            return true;
        }

        protected override IEnumerable<Type> ComplexTypes
        {
            get
            {
                return this._domainServiceDescriptionAggregate.ComplexTypes;
            }
        }

        protected override bool IsDerivedType
        {
            get
            {
                return (this._visibleBaseType != null);
            }
        }

        private class DomainServiceDescriptionAggregate
        {
            private HashSet<Type> _complexTypes;
            private HashSet<Type> _entityTypes;

            internal DomainServiceDescriptionAggregate(IEnumerable<DomainServiceDescription> domainServiceDescriptions)
            {
                this.DomainServiceDescriptions = domainServiceDescriptions;
                this._complexTypes = new HashSet<Type>();
                this._entityTypes = new HashSet<Type>();
                foreach (DomainServiceDescription description in domainServiceDescriptions)
                {
                    foreach (Type type in description.ComplexTypes)
                    {
                        this._complexTypes.Add(type);
                    }
                    foreach (Type type2 in description.EntityTypes)
                    {
                        this._entityTypes.Add(type2);
                    }
                }
            }

            internal Type GetEntityBaseType(Type entityType)
            {
                Type baseType = entityType.BaseType;
                while (baseType != null)
                {
                    if (this.EntityTypes.Contains<Type>(baseType))
                    {
                        return baseType;
                    }
                    baseType = baseType.BaseType;
                }
                return baseType;
            }

            internal Type GetRootEntityType(Type entityType)
            {
                Type type = null;
                while (entityType != null)
                {
                    if (this.EntityTypes.Contains<Type>(entityType))
                    {
                        type = entityType;
                    }
                    entityType = entityType.BaseType;
                }
                return type;
            }

            internal IEnumerable<Type> ComplexTypes
            {
                get
                {
                    foreach (Type iteratorVariable0 in this._complexTypes)
                    {
                        yield return iteratorVariable0;
                    }
                }
            }

            internal IEnumerable<DomainServiceDescription> DomainServiceDescriptions { get; private set; }

            internal IEnumerable<Type> EntityTypes
            {
                get
                {
                    foreach (Type iteratorVariable0 in this._entityTypes)
                    {
                        yield return iteratorVariable0;
                    }
                }
            }

            internal bool IsShared
            {
                get
                {
                    return (this.DomainServiceDescriptions.Count<DomainServiceDescription>() > 1);
                }
            }


        }
    }
}

